/*
 * Copyright (C) 2008 Universidade Federal de Campina Grande
 *  
 * This file is part of OurGrid. 
 *
 * OurGrid is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free 
 * Software Foundation, either version 3 of the License, or (at your option) 
 * any later version. 
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details. 
 * 
 * You should have received a copy of the GNU Lesser General Public License 
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 * 
 */
package org.ourgrid.peer.controller.allocation;

import static org.easymock.EasyMock.createMock;

import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import junit.framework.TestCase;

import org.easymock.EasyMock;
import org.ourgrid.acceptance.util.WorkerAcceptanceUtil;
import org.ourgrid.acceptance.worker.WorkerAcceptanceTestCase;
import org.ourgrid.common.interfaces.LocalWorkerProvider;
import org.ourgrid.common.interfaces.LocalWorkerProviderClient;
import org.ourgrid.common.interfaces.to.LocalWorkerState;
import org.ourgrid.common.interfaces.to.RequestSpec;
import org.ourgrid.common.spec.job.JobSpec;
import org.ourgrid.common.spec.worker.WorkerSpec;
import org.ourgrid.peer.PeerComponentContextFactory;
import org.ourgrid.peer.controller.LocalWorkerProviderController;
import org.ourgrid.peer.to.AllocableWorker;
import org.ourgrid.peer.to.Consumer;
import org.ourgrid.peer.to.LocalAllocableWorker;
import org.ourgrid.peer.to.LocalConsumer;
import org.ourgrid.peer.to.LocalWorker;
import org.ourgrid.peer.to.PeerBalance;
import org.ourgrid.peer.to.Priority;
import org.ourgrid.peer.to.RemoteConsumer;
import org.ourgrid.peer.to.Request;
import org.ourgrid.peer.to.Priority.Range;

import br.edu.ufcg.lsd.commune.container.contextfactory.PropertiesFileParser;
import br.edu.ufcg.lsd.commune.functionaltests.util.TestContext;
import br.edu.ufcg.lsd.commune.identification.ContainerID;
import br.edu.ufcg.lsd.commune.identification.DeploymentID;
import br.edu.ufcg.lsd.commune.identification.ServiceID;

public class DefaultAllocatorTest extends TestCase {
	
	protected WorkerAcceptanceUtil workerAcceptanceUtil = new WorkerAcceptanceUtil(createComponentContext());
	
	protected TestContext createComponentContext() {
		return new TestContext(
				new PeerComponentContextFactory(
						new PropertiesFileParser(WorkerAcceptanceTestCase.PROPERTIES_FILEPATH
						)).createContext());
	}
	
	@Override
	public void setUp() throws Exception {
		super.setUp();
	}
	
	@Override
	public void tearDown() throws Exception {
		super.tearDown();
	}

	/**
	 * This method was created to catch a bug on DefaultAllocator class method getRangeBasedPriorityAllocation.
	 * In case there was a higher priority to be checked and the consumer still needed workers, the method
	 * treated as if that priority were a lower one.
	 * When there isn't any lower or equal priority compared to the consumer's, it must not search for
	 * workers anymore. It used to stop searching only either if there weren't any priorities left or the current
	 * searching consumer has the same priority as my consumer. 
	 * 
	 *  The following code was added in order to correct the bug.
	 *  
	 *  if(requestPriority.compareTo(currentPriorityRange) < 0) {
	 *		break;
	 *	}
	 */
	public void testGetRangeBasedPriorityAllocation() {
		
		// Allocator initialization
		DefaultAllocator defaultAllocator = DefaultAllocator.getInstance();
		
		// Parameters initialization
		Consumer localConsumer = new LocalConsumer();
		String consumerPubKey = "consumerPubKey";
		DeploymentID consumerID = new DeploymentID(new ContainerID("consumerName", "consumerServer", "consumerModule", consumerPubKey),
			"consumerObjName");
		localConsumer.setConsumerStub(consumerID, localConsumer);
		
		Consumer remoteConsumer = new RemoteConsumer();
		String remoteConsumerPubKey = "remConsumerPubKey";
		
		DeploymentID remConsumerID = new DeploymentID(new ContainerID("remConsumerName", "remConsumerServer", "remConsumerModule", 
				remoteConsumerPubKey), "remConsumerObjName");
		remoteConsumer.setConsumerStub(remConsumerID, remoteConsumer);
			
		String requirements = "";
		Priority requestPriority = new Priority(Range.ALLOC_FOR_UNKNOWN_COMMUNITY);
		int requestNecessity = 10;
		
		String workerServerName = "xmpp.ourgrid.org";

		String workerAUserName = "workerA";
		WorkerSpec workerSpecA = new WorkerSpec();
		workerSpecA.putAttribute(WorkerSpec.ATT_MEM, "16");
		workerSpecA.putAttribute(WorkerSpec.ATT_USERNAME, workerAUserName);
		workerSpecA.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);
		
		String workerBUserName = "workerB";
		WorkerSpec workerSpecB = new WorkerSpec();
		workerSpecB.putAttribute(WorkerSpec.ATT_MEM, "32");
		workerSpecB.putAttribute(WorkerSpec.ATT_USERNAME, workerBUserName);
		workerSpecB.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);

		String workerCUserName = "workerC";
		WorkerSpec workerSpecC = new WorkerSpec();
		workerSpecC.putAttribute(WorkerSpec.ATT_MEM, "64");
		workerSpecC.putAttribute(WorkerSpec.ATT_USERNAME, workerCUserName);
		workerSpecC.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);

		String workerDUserName = "workerD";
		WorkerSpec workerSpecD = new WorkerSpec();
		workerSpecD.putAttribute(WorkerSpec.ATT_MEM, "128");
		workerSpecD.putAttribute(WorkerSpec.ATT_USERNAME, workerDUserName);
		workerSpecD.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);

		String workerEUserName = "workerE";
		WorkerSpec workerSpecE = new WorkerSpec();
		workerSpecE.putAttribute(WorkerSpec.ATT_MEM, "256");
		workerSpecE.putAttribute(WorkerSpec.ATT_USERNAME, workerEUserName);
		workerSpecE.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);

		String workerFUserName = "workerF";
		WorkerSpec workerSpecF = new WorkerSpec();
		workerSpecF.putAttribute(WorkerSpec.ATT_MEM, "512");
		workerSpecF.putAttribute(WorkerSpec.ATT_USERNAME, workerFUserName);
		workerSpecF.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);

		String workerGUserName = "workerG";
		WorkerSpec workerSpecG = new WorkerSpec();
		workerSpecG.putAttribute(WorkerSpec.ATT_MEM, "1024");
		workerSpecG.putAttribute(WorkerSpec.ATT_USERNAME, workerGUserName);
		workerSpecG.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);

		String workerHUserName = "workerH";
		WorkerSpec workerSpecH = new WorkerSpec();
		workerSpecH.putAttribute(WorkerSpec.ATT_MEM, "2048");
		workerSpecH.putAttribute(WorkerSpec.ATT_USERNAME, workerHUserName);
		workerSpecH.putAttribute(WorkerSpec.ATT_SERVERNAME, workerServerName);

		Collection<AllocableWorker> allAllocableWorkers = new LinkedList<AllocableWorker>();
		LocalWorkerProvider lwp = new LocalWorkerProviderController();
		
		LocalWorker workerA = new LocalWorker(workerSpecA, new ServiceID("workerA", "serverA", "worker", "worker"));
		AllocableWorker allocA = new LocalAllocableWorker(workerA, lwp, "CN=workerA");
		allAllocableWorkers.add(allocA);
				
		LocalWorker workerB = new LocalWorker(workerSpecB, new ServiceID("workerB", "serverB", "worker", "worker"));
		AllocableWorker allocB = new LocalAllocableWorker(workerB, lwp, "CN=workerB");
		allAllocableWorkers.add(allocB);

		LocalWorker workerC = new LocalWorker(workerSpecC, new ServiceID("workerC", "serverC", "worker", "worker"));
		AllocableWorker allocC = new LocalAllocableWorker(workerC, lwp, "CN=workerC");
		allocC.setStatus(LocalWorkerState.IN_USE);
		LocalWorkerProviderClient client = createMock(LocalWorkerProviderClient.class);
		workerAcceptanceUtil.createStub(client, LocalWorkerProviderClient.class, consumerID);
		
		EasyMock.replay(client);
		Request requestC = new Request(new RequestSpec(0, new JobSpec("label"), 123, "", 5, 0, 0));
		requestC.addAllocableWorker(allocC);
		allocC.setRequest(requestC);
		allocC.setConsumer(localConsumer);
		((LocalConsumer)localConsumer).addRequest(requestC);		
		allAllocableWorkers.add(allocC);

		LocalWorker workerD = new LocalWorker(workerSpecD, new ServiceID("workerD", "serverD", "worker", "worker"));
		AllocableWorker allocD = new LocalAllocableWorker(workerD, lwp, "CN=workerD");
		allAllocableWorkers.add(allocD);

		LocalWorker workerE = new LocalWorker(workerSpecE, new ServiceID("workerE", "serverE", "worker", "worker"));
		AllocableWorker allocE = new LocalAllocableWorker(workerE, lwp, "CN=workerE");
		allAllocableWorkers.add(allocE);

		LocalWorker workerF = new LocalWorker(workerSpecF, new ServiceID("workerF", "serverF", "worker", "worker"));
		AllocableWorker allocF = new LocalAllocableWorker(workerF, lwp, "CN=workerF");
		allAllocableWorkers.add(allocF);

		LocalWorker workerG = new LocalWorker(workerSpecG, new ServiceID("workerG", "serverG", "worker", "worker"));
		AllocableWorker allocG = new LocalAllocableWorker(workerG, lwp, "CN=workerG");
		allAllocableWorkers.add(allocG);

		LocalWorker workerH = new LocalWorker(workerSpecH, new ServiceID("workerH", "serverH", "worker", "worker"));
		AllocableWorker allocH = new LocalAllocableWorker(workerH, lwp, "CN=workerH");
		allAllocableWorkers.add(allocH);
		
		List<AllocableWorker> result = defaultAllocator.getRangeBasedPriorityAllocation(remoteConsumer,
				allAllocableWorkers, requirements, requestPriority, requestNecessity, new HashMap<String, PeerBalance>(), requestC.getSpec().getAnnotations());
		
		EasyMock.verify(client);
		
		Collection<AllocableWorker> expectedResult = new LinkedList<AllocableWorker>();
		expectedResult.add(allocA);
		expectedResult.add(allocB);
		expectedResult.add(allocD);
		expectedResult.add(allocE);
		expectedResult.add(allocF);
		expectedResult.add(allocG);
		expectedResult.add(allocH);
				
		assertTrue(result.containsAll(expectedResult) && expectedResult.containsAll(result));
	}

}
